<?php

class Indexer_Document implements Indexer_Searchable
{
    protected $uri        = '';
    protected $score      = 0;
    protected $fields     = array();
    protected $indexTypes = array();
    
    const INDEX_TYPE_UNSTORED  = 'UnStored';
    const INDEX_TYPE_KEYWORD   = 'Keyword';
    const INDEX_TYPE_BINARY    = 'Binary';
    const INDEX_TYPE_TEXT      = 'Text';
    const INDEX_TYPE_UNINDEXED = 'UnIndexed';
    
    public static $useHtmlExtractor = false;
    public static $abstractMaxChars = 200;
    
    public function __construct($uri='')
    {
        if (empty($uri)) {
            throw new Indexer_Exception('Every document must have a Unique Resource Identifier, but none was provided.');
        }
        $this->uri = $uri;
    }
    
    public function setField($fieldName, $value, $indexType=false)
    {
        $this->fields[$fieldName] = $value;
        if ($indexType !== false) {
            // add validation of index type against class constants (see Lucene field types)
            $this->indexTypes[$fieldName] = $indexType;
        }
    }
    
    public function getURI()
    {
        return $this->uri;
    }
    
    public function setScore($score)
    {
        $this->score = $score;
    }
    
    public function getScore()
    {
        return $this->score;
    }
    
    public function getField($fieldName)
    {
        if (!isset($this->fields[$fieldName])) {
            throw new Indexer_Exception("Field '$fieldName' does not exist in document.");
        }
        return $this->fields[$fieldName];
    }
    
    public function getFieldNames()
    {
        return array_keys($this->fields);
    }
    
    public function getIndexType($fieldName)
    {
        if (!isset($this->indexTypes[$fieldName])) {
            return self::INDEX_TYPE_TEXT;
        }
        return $this->indexTypes[$fieldName];
    }
    
    public function setIndexType($fieldName, $type)
    {
        $validTypes = array(self::INDEX_TYPE_BINARY,
                            self::INDEX_TYPE_KEYWORD,
                            self::INDEX_TYPE_TEXT,
                            self::INDEX_TYPE_UNINDEXED,
                            self::INDEX_TYPE_UNSTORED);
        
        if (in_array($type, $validTypes)) {
            $this->indexTypes[$fieldName] = $type;
            return true;
        } else {
            throw new Indexer_Exception("'$type' is not a valid field type.");
        }
    }
    
    /**
     * @param $xml A SimpleXML object containing the relevant elements
     * @return Indexer_Document
     * @TODO Don't restrict access to SimpleXML, allow raw XML strings and DOM objects as well
     */
    public static function fromXML(SimpleXMLElement $xml)
    {
        if (isset($xml->uri)) {
            throw new Indexer_Exception('Document does not contain an "uri" element.');
        }
        $doc = new Indexer_Document((string)$xml->uri);
        $fields = $xml->xpath('//field');
        foreach($fields as $field) {
            $doc->setField((string)$field['name'], (string)$field);
        }
        return $doc;
    }
    
    /**
     * Uses the HTML parsing capabilities of Zend_Search_Lucene
     * to extract meta-information from a string containing a HTML
     * document. Information is stored in Marjory's default field set:
     *  * title    (Text)
     *  * content  (UnStored)
     *  * abstract (UnIndexed)
     *
     * Alternatively, you can use your own HTML parsing class to
     * extract the fields as desired, by setting the static property
     * $useHtmlExtractor to the name of your parser class.
     * The class must implement the public method "getFieldValue($fieldName)" 
     * and provide information on the fields "title", "content" and "abstract". 
     * The class constructor must accept the HTML document as a string.
     * 
     * If the static property $abstractMaxChars is set, the abstract will
     * be shortened to the desired length.
     * 
     * @param string $uri
     * @param string $htmlDocument
     * @return Indexer_Document 
     */
    public static function fromHTML($uri, $htmlDocument)
    {
        if (self::$useHtmlExtractor && class_exists(self::$useHtmlExtractor, true)) {
            //error_log('Using custom HtmlExctractor');
            $htmlDoc = new self::$useHtmlExtractor($htmlDocument);
        } else {
            //error_log('Using Zend_Search_Lucene HTML extraction');
            $htmlDoc = Zend_Search_Lucene_Document_Html::loadHTML($htmlString);
        }
        $doc = new Indexer_Document($uri);
        $doc->setField('title', $htmlDoc->getFieldValue('title'));
        $doc->setIndexType('title', self::INDEX_TYPE_TEXT);
        if (self::$useHtmlExtractor && class_exists(self::$useHtmlExtractor, true)) {
            $contentField = 'content';
        } else {
            $contentField = 'body'; // for Lucene parser
        }
        //error_log('Using content field: '.$contentField);
        $doc->setField('content', $htmlDoc->getFieldValue($contentField));
        $doc->setIndexType('content', self::INDEX_TYPE_UNSTORED);
        $abstract = $htmlDoc->getFieldValue($contentField);
        if (self::$abstractMaxChars) {
            $abstract = substr($abstract, 0, self::$abstractMaxChars);
        }
        $doc->setField('abstract', $abstract);
        $doc->setIndexType('abstract', self::INDEX_TYPE_UNINDEXED);
        return $doc;
    }
    
    /* @TODO Add methods fromJSON, fromArray, fromWhatever... and maybe a factory method with input type auto-detection :-) */
}